package com.ecar.oauth.provider.beans;

import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.security.oauth2.common.util.OAuth2Utils;
import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.DefaultSecurityContextAccessor;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.SecurityContextAccessor;
import org.springframework.security.oauth2.provider.TokenRequest;

import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

/**
 * 解决多个scope用逗号分隔无法解析的问题
 * Created by 30 on 2017/4/10.
 */
public class OAuth2CustomRequestFactory implements org.springframework.security.oauth2.provider.OAuth2RequestFactory {

	private final ClientDetailsService clientDetailsService;
	private SecurityContextAccessor securityContextAccessor = new DefaultSecurityContextAccessor();
	private boolean checkUserScopes = false;

	public OAuth2CustomRequestFactory(ClientDetailsService clientDetailsService) {
		this.clientDetailsService = clientDetailsService;
	}

	public void setSecurityContextAccessor(SecurityContextAccessor securityContextAccessor) {
		this.securityContextAccessor = securityContextAccessor;
	}

	public void setCheckUserScopes(boolean checkUserScopes) {
		this.checkUserScopes = checkUserScopes;
	}

	public AuthorizationRequest createAuthorizationRequest(Map<String, String> authorizationParameters) {
		String clientId = authorizationParameters.get("client_id");
		String state = authorizationParameters.get("state");
		String redirectUri = authorizationParameters.get("redirect_uri");
		Set responseTypes = OAuth2Utils.parseParameterList(authorizationParameters.get("response_type"));
		Set scopes = extractScopes(authorizationParameters, clientId);
		AuthorizationRequest request =
			new AuthorizationRequest(authorizationParameters, null, clientId, scopes, Collections.<String>emptySet(),
									 null, false, state, redirectUri, responseTypes);
		ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
		request.setResourceIdsAndAuthoritiesFromClientDetails(clientDetails);
		return request;
	}

	public OAuth2Request createOAuth2Request(AuthorizationRequest request) {
		return request.createOAuth2Request();
	}

	public TokenRequest createTokenRequest(Map<String, String> requestParameters, ClientDetails authenticatedClient) {
		String clientId = requestParameters.get("client_id");
		if (clientId == null) {
			clientId = authenticatedClient.getClientId();
		} else if (!clientId.equals(authenticatedClient.getClientId())) {
			throw new InvalidClientException("Given client ID does not match authenticated client");
		}

		String grantType = requestParameters.get("grant_type");
		Set scopes = extractScopes(requestParameters, clientId);
		return new TokenRequest(requestParameters, clientId, scopes, grantType);
	}

	public TokenRequest createTokenRequest(AuthorizationRequest authorizationRequest, String grantType) {
		return new TokenRequest(authorizationRequest.getRequestParameters(),
								authorizationRequest.getClientId(),
								authorizationRequest.getScope(), grantType);
	}

	public OAuth2Request createOAuth2Request(ClientDetails client, TokenRequest tokenRequest) {
		return tokenRequest.createOAuth2Request(client);
	}

	private Set<String> extractScopes(Map<String, String> requestParameters, String clientId) {
		Set scopes = parseParameterList(requestParameters.get("scope"));
		ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
		if (scopes == null || scopes.isEmpty()) scopes = clientDetails.getScope();

		if (checkUserScopes) scopes = checkUserScopes(scopes, clientDetails);

		return scopes;
	}

	/** @noinspection WhileLoopReplaceableByForEach, UnusedParameters */
	private Set<String> checkUserScopes(Set<String> scopes, ClientDetails clientDetails) {
		if (!securityContextAccessor.isUser()) return scopes;

		LinkedHashSet result = new LinkedHashSet();
		Set authorities = AuthorityUtils.authorityListToSet(securityContextAccessor.getAuthorities());
		Iterator var5 = scopes.iterator();

		while(var5.hasNext()) {
			String scope = (String) var5.next();
			if (!authorities.contains(scope)
				&& !authorities.contains(scope.toUpperCase())
				&& !authorities.contains("ROLE_" + scope.toUpperCase()))
				continue;

			result.add(scope);
		}

		return result;
//		while (true) {
//			String scope;
//			do {
//				if (!var5.hasNext()) return result;
//
//				scope = (String) var5.next();
//			} while (!authorities.contains(scope) && !authorities.contains(scope.toUpperCase()) && !authorities.contains("ROLE_" + scope.toUpperCase()));
//
//			result.add(scope);
//		}
	}

	private Set<String> parseParameterList(String values) {
		TreeSet result = new TreeSet();
		if(values != null && values.trim().length() > 0) {
			String[] tokens = values.split("[\\s+,]");
			result.addAll(Arrays.asList(tokens));
		}

		return result;
	}
}
